package cn.icare.cms.impl;

import cn.icare.cms.*;
import cn.icare.cms.model.vo.rest.MsgDatasVO;
import cn.icare.cms.model.vo.rest.RespMsgVO;
import cn.icare.cms.model.wx.enums.ErrorCodeEnum;
import cn.icare.cms.model.wx.ro.GetSessionRo;
import cn.icare.cms.model.wx.ro.WxConfig;
import cn.icare.cms.model.wx.ro.WxPayInfoRo;
import cn.icare.cms.model.wx.vo.WxPayInfoVo;
import cn.icare.cms.model.wx.vo.WxUserInfoVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.Base64;


@Service
public class WxServiceImpl implements WxService {

    private final HTTPUtil httpUtil = new HTTPUtil(true, HTTPUtil.CONTENTTYPE_JSON);


    private final AESUtil aesUtil = new AESUtil();
    private final Logger logger = LoggerFactory.getLogger(WxServiceImpl.class);

    @Override
    public WxUserInfoVo getSessionByCode(WxConfig config, GetSessionRo ro) {

        try {
            ro.setUrl("sns/jscode2session");
            ro.setGrant_type("authorization_code");
            String url = config.getWxapi() + ro.getUrl();
            url += "?appid=" + config.getAppid() + "&secret=" + config.getSecret();
            url += String.format("&js_code=%s&grant_type=%s", ro.getJs_code(), ro.getGrant_type());
            String result = httpUtil.sendRequestGet(url);
            if (StringUtil.isNotEmpty(result)) {
                JSONObject jsonObject = JSONObject.parseObject(result);
                WxUserInfoVo vo = JSON.parseObject(jsonObject.toJSONString(), WxUserInfoVo.class);
                return vo;
            }
        } catch (Exception ex) {
            logger.error(String.format("getSessionByCode异常:%s", ex
                    .getMessage()));
        }
        return null;
    }

    @Override
    public String getWXDecodeDate(WxConfig config, GetSessionRo ro, String encryptedData, String iv) {
        String result = "";
        WxUserInfoVo userInfoVo = this.getSessionByCode(config, ro);
        if (userInfoVo == null) {
            logger.error("获取用户session失败");
        } else {
            if (userInfoVo.getSession_key() != null && StringUtil.isNotEmpty(userInfoVo.getSession_key())) {
                String sessionKey = userInfoVo.getSession_key();
                logger.info(String.format("data:%s", encryptedData));
                logger.info(String.format("sessionKey:%s", sessionKey));
                logger.info(String.format("iv:%s", iv));
                RespMsgVO response = this.getASEDecodeData(sessionKey, encryptedData, iv);
                if (response.getCode() != ErrorCodeEnum.OK.getCode()) {
                    logger.error("解码异常：" + response.getMsg());
                } else {
                    result = response.getResData().toString();
                }
            } else {
                logger.error(String.format("获取用户session失败,出错信息：%s", userInfoVo.getErrmsg()));
            }

        }
        logger.info(String.format("%s解码成功结果是:%s", encryptedData, result));
        return result;
    }


    @Override
    public RespMsgVO getASEDecodeData(String sessionKey, String encryptedData, String iv) {
        RespMsgVO respMsgVO = new RespMsgVO(ErrorCodeEnum.DECODEFAILE.getCode(), "", null);

        Base64.Decoder decoder = Base64.getDecoder();
        if (sessionKey.length() != 24) {
            //session_key非法
            respMsgVO.setCode(ErrorCodeEnum.WRONGFULKey.getCode());
            respMsgVO.setMsg(ErrorCodeEnum.WRONGFULKey.getName());
        }
        byte[] aesKey = decoder.decode(sessionKey);
        if (iv.length() != 24) {
            //初始向量非法

            respMsgVO.setCode(ErrorCodeEnum.IV_WRONG.getCode());
            respMsgVO.setMsg(ErrorCodeEnum.IV_WRONG.getName());
        }
        byte[] ivKey = decoder.decode(iv);
        byte[] aesData = decoder.decode(encryptedData);
        //调用aes解码算法
        byte[] result = aesUtil.decrypt(aesData, aesKey, ivKey);
        if (result == null) {
            //解密失败
            respMsgVO.setCode(ErrorCodeEnum.DECODEFAILE.getCode());
            respMsgVO.setMsg(ErrorCodeEnum.DECODEFAILE.getName());
        } else {
            respMsgVO.setCode(ErrorCodeEnum.OK.getCode());
            respMsgVO.setMsg("");
            respMsgVO.setResData(new String(result));

        }
        return respMsgVO;
    }


    @Override
    public WxPayInfoVo sendPay(String payApiUri, WxPayInfoRo ro) {

        WxPayInfoVo wxPayInfoVo = null;
        try {
            String xmlData = XmlUtil.serializer(ro);

            this.logger.info("sendPay uri:" + payApiUri);
            this.logger.info("sendPay xmlData:" + xmlData);
            String result = httpUtil.sendRequestPostXml(payApiUri, xmlData);
            this.logger.info("sendPay result:" + result);
            if (!StringUtil.isEmpty(result)) {

                wxPayInfoVo = (WxPayInfoVo) XmlUtil.deserialize(result, WxPayInfoVo.class);
                this.logger.info("wxPayInfoVo:" + JSON.toJSONString(wxPayInfoVo));

            }

        } catch (Exception ex) {
            this.logger.error(String.format("sendPay 异常:%s", ex.getMessage()));
            ex.printStackTrace();
        }
        return wxPayInfoVo;
    }

    @Override
    public WxPayInfoVo refund(String apiUri, WxPayInfoRo ro,String certPath) {


        WxPayInfoVo wxPayInfoVo = null;
        try {
            String xmlData = XmlUtil.serializer(ro);

            this.logger.info("refund uri:" + apiUri);
            this.logger.info("refund xmlData:" + xmlData);
            String result = CertHttpUtil.sendRequestPostXml(apiUri, xmlData,ro.getMch_id(),certPath);
            this.logger.info("refund result:" + result);
            if (!StringUtil.isEmpty(result)) {

                wxPayInfoVo = (WxPayInfoVo) XmlUtil.deserialize(result, WxPayInfoVo.class);
                this.logger.info("wxPayInfoVo:" + JSON.toJSONString(wxPayInfoVo));

            }

        } catch (Exception ex) {
            this.logger.error(String.format("refund 异常:%s", ex.getMessage()));
            ex.printStackTrace();
        }
        return wxPayInfoVo;
    }


    @Override
    public String getOpenIdByCode(WxConfig config, String code) {

        GetSessionRo ro = new GetSessionRo();
        ro.setJs_code(code);
        WxUserInfoVo userInfoVo = this.getSessionByCode(config, ro);
        if (userInfoVo != null) {
            return userInfoVo.getOpenid();
        }
        return null;
    }
}
